﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Simulator
{
	public partial class MstConfigIdForm : Form
	{
		class VlanAndMstId : INotifyPropertyChanged
		{
			readonly ushort _vlan;
			byte _mstid;

			public VlanAndMstId (ushort vlan, byte mstid)
			{
				Debug.Assert ((vlan >= 1) && (vlan <= 4094));
				Debug.Assert (mstid <= 64);
				_vlan = vlan;
				_mstid = mstid;
			}

			public event PropertyChangedEventHandler PropertyChanged;

			public ushort VLAN
			{
				get { return _vlan; }
			}

			public byte MSTID
			{
				get { return _mstid; }

				set
				{
					if (value != _mstid)
					{
						Debug.Assert (value <= 64);
						_mstid = value;

						if (PropertyChanged != null)
							PropertyChanged (this, new PropertyChangedEventArgs ("MSTID"));
					}
				}
			}
		}

		readonly Bridge _bridge;
		readonly BindingList<VlanAndMstId> _bindingList;

		public MstConfigIdForm (Bridge bridge)
		{
			InitializeComponent ();

			// For proper scaling, we must set the font _after_ calling InitializeComponent.
			var font = new Font ("Segoe UI", 9);
			if (font.OriginalFontName != font.Name)
			{
				// Requested font name doesn't exist on the system. We're probably on Windows XP. Let's fallback to Tahoma.
				font.Dispose ();
				font = new Font ("Tahoma", 9);
			}
			this.Font = font;

			_bridge = bridge;
			_bindingList = new BindingList<VlanAndMstId> ();

			// name
			textBoxName.Text = bridge.Wrapper.GetMstConfigName ();
			labelName.ForeColor = SystemColors.ControlText;
			buttonRevertName.Enabled = false;
			buttonUseDefaultName.Enabled = !bridge.Wrapper.GetMstConfigDefaultNameUsed ();

			// revision level
			ushort revisionLevel = bridge.Wrapper.GetMstConfigRevisionLevel ();
			numericUpDownRevision.Value = revisionLevel;

			// config table
			var mstids = _bridge.Wrapper.GetMstConfigTable ();
			_bindingList = new BindingList<VlanAndMstId> ();
			for (ushort i = 0; i < 4094; i++)
				_bindingList.Add (new VlanAndMstId ((ushort) (i + 1), mstids [i]));

			_bindingList.ListChanged += OnBindingListChanged;

			//
			dataGridViewConfig.DataSource = _bindingList;
			dataGridViewConfig.Columns ["VLAN" ].DisplayIndex = 0;
			dataGridViewConfig.Columns ["MSTID"].DisplayIndex = 1;
		}

		private void textBoxName_TextChanged (object sender, EventArgs e)
		{
			bool nameChanged = (textBoxName.Text != _bridge.Wrapper.GetMstConfigName ());
			if (nameChanged)
			{
				labelName.ForeColor = Color.Red;
				buttonRevertName.Enabled = true;
			}
			else
			{
				labelName.ForeColor = SystemColors.ControlText;
				buttonRevertName.Enabled = false;
			}

			buttonUseDefaultName.Enabled = true;
		}

		private void buttonRevertName_Click (object sender, EventArgs e)
		{
			string previousName = _bridge.Wrapper.GetMstConfigName ();

			bool nameChanged = (textBoxName.Text != previousName);
			Debug.Assert (nameChanged);

			textBoxName.Text = previousName;
			// buttonRevertName.Enabled and labelName.ForeColor are set in textBoxName_TextChanged
			buttonUseDefaultName.Enabled = !_bridge.Wrapper.GetMstConfigDefaultNameUsed ();

			textBoxName.Focus ();
		}

		private void buttonUseDefaultName_Click (object sender, EventArgs e)
		{
			textBoxName.Text = _bridge.Wrapper.GetMstConfigDefaultName ();
			// buttonRevertName.Enabled and labelName.ForeColor are set in textBoxName_TextChanged
			buttonUseDefaultName.Enabled = false;

			textBoxName.Focus ();
		}

		void OnBindingListChanged (object sender, ListChangedEventArgs e)
		{
			if (IsConfigTableChanged ())
			{
				labelConfig.ForeColor = Color.Red;
				buttonRevertConfig.Enabled = true;
			}
			else
			{
				labelConfig.ForeColor = SystemColors.ControlText;
				buttonRevertConfig.Enabled = false;
			}
		}

		bool IsConfigTableChanged ()
		{
			var mstids = _bridge.Wrapper.GetMstConfigTable ();
			for (int i = 0; i < 4094; i++)
			{
				if (mstids [i] != _bindingList [i].MSTID)
					return true;
			}

			return false;
		}

		protected override void OnFormClosing (FormClosingEventArgs e)
		{
			base.OnFormClosing (e);

			if (this.DialogResult != DialogResult.OK)
				return;

			uint timestamp = (uint) Environment.TickCount;

			bool usingDefaultName = !buttonUseDefaultName.Enabled;

			if (usingDefaultName)
			{
				lock (_bridge.WrapperLock)
					_bridge.Wrapper.SetMstConfigDefaultName (timestamp);
			}
			else
			{
				String newName = textBoxName.Text;
				if (newName != _bridge.Wrapper.GetMstConfigName ())
				{
					if (newName.Length > 32)
					{
						MessageBox.Show (this, "Name cannot be longer than 32 characters");
						textBoxName.Focus ();
						e.Cancel = true;
						return;
					}

					for (int i = 0; i < newName.Length; i++)
					{
						if ((newName [i] < 32) || (newName [i] >= 128))
						{
							MessageBox.Show (this, "Name must only contain ASCII characters (32..127)");
							textBoxName.Focus ();
							e.Cancel = true;
							return;
						}
					}

					lock (_bridge.WrapperLock)
						_bridge.Wrapper.SetMstConfigName (newName, timestamp);
				}
			}

			if (numericUpDownRevision.Value != _bridge.Wrapper.GetMstConfigRevisionLevel ())
			{
				lock (_bridge.WrapperLock)
					_bridge.Wrapper.SetMstConfigRevisionLevel ((ushort) numericUpDownRevision.Value, timestamp);
			}

			if (IsConfigTableChanged ())
			{
				// check that the user is not attempting to map a VLAN to a non-existant tree.
				foreach (var vm in _bindingList)
				{
					if (vm.MSTID >= _bridge.TreeCount)
					{
						string str = String.Format ("The bridge was created with only {0} trees (tree index 0 to {1}), but you are attempting to map VLAN {2} to tree index {3}. "
						+ "Either map the VLAN to a tree number that exists in the bridge, or delete and recreate the bridge with enough trees.",
						_bridge.TreeCount, _bridge.TreeCount - 1, vm.VLAN, vm.MSTID);

						MessageBox.Show (this, str);
						dataGridViewConfig.Focus ();
						e.Cancel = true;
						return;
					}
				}

				byte[] mstids = new byte [4094];
				for (int i = 0; i < 4094; i++)
					mstids [i] = _bindingList [i].MSTID;

				lock (_bridge.WrapperLock)
					_bridge.Wrapper.SetMstConfigTable (mstids, timestamp);
			}
		}

		private void buttonRevertConfig_Click (object sender, EventArgs e)
		{
			Debug.Assert (IsConfigTableChanged ());

			var mstids = _bridge.Wrapper.GetMstConfigTable ();

			for (int i = 0; i < 4094; i++)
				_bindingList [i].MSTID = mstids [i];
		}

		private void buttonUseDefaultConfig_Click (object sender, EventArgs e)
		{
			foreach (VlanAndMstId vm in _bindingList)
				vm.MSTID = 0;
		}
	}
}
